/*************************************************************************/
/*************************************************************************/
/*************************************************************************/

/*
**
** Copyright (c) 2010 by Lorimark Solutions, LLC.
** All Rights Reserved.
** Send inquiries to: info@lorimarksolutions.com
**
*/

#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sfr_defs.h>

#include "mega164serial.h"


/*!
** \brief Serial Port Interrupt Service Routines
**
** The following routines take care of handling the 
**  interrupt service procedures for the serial ports.
**  There are two separate circular lists for serial
**  data; Receive and Transmit.
**
** The code between the two RX/TX circular list managers 
**  may look redundant, and that's only because it is.  
**  The reason being is that since these are interrupt 
**  driven routines, the goal is to have them function 
**  as quickly as possible, and having them suffer the 
**  additional lag of relative indexes to relative 
**  buffers is a unreasonably high cost.  So, we have 
**  traded code size for speed.  In other words, these 
**  routines will occupy more code memory than they might, 
**  but they will also run blindingly fast.
**
** Note that in most of the procedures below, interrupts
**  are turned off before the lists are manipulated, and 
**  left off.  This is an important issue, because some of
**  these codes are used in the interrupt service procedures
**  themselves, and we don't want to inadvertently turn
**  interrupts back on when we're simply massaging the
**  circular lists.  Therefore, in your code, when you
**  use these routines, you will have to turn interrupts
**  back on after manipulating the lists.
**
** Note that the serial I/O lines are inactive high.  This
**  means no data is moving when the pins are in the high
**  state.
*/

/*
** Set up the buffer structure.
**
*/
typedef struct
{
  uint8 status;
  uint8 current;
  uint8 current_top;
  uint8 next_bottom;
  uint8 items[USART_BUFFER_SIZE];
} usart_t;

usart_t rxUsart[2];

/*
** This resets the reset usart, and also sets up the interrupt
**  conditions.
**
*/
void rxUsartReset(uint8 channel)
{
  DISABLE_INTERRUPTS;
  rxUsart[channel].status       = 0;
  rxUsart[channel].current      = 0;
  rxUsart[channel].current_top  = 0;
  rxUsart[channel].next_bottom  = 0;

  //setup interrupts here
}

/*!
** \brief Serial List Item Count
**
** \return Number of items currently in the list
**
*/
//#inline
uint8 rxUsartCount(uint8 channel)
{
  return( rxUsart[channel].current );
}

/*!
** \brief Serial List Space Remaining
**
** \return Number of empty slots available in the list
*/
//#inline
uint8 rxUsartSpace(uint8 channel)
{
  return( USART_BUFFER_SIZE - rxUsart[channel].current );
}

/*!
** \brief Put an item into the Serial List
**
**
*/
void rxUsartPut( uint8 channel, uint8 data )
{
   /*
   ** Cannot allow interrupts to occur while massaging this circular
   **  buffer's registers.
   **
   */
   DISABLE_INTERRUPTS;

   /*
   ** Only add the item to the list if there is room for it
   **
   */
   if( rxUsart[channel].current < USART_BUFFER_SIZE )
   {
      /*
      ** We're adding an item so we must bump the item count
      **
      */
      rxUsart[channel].current++;

      /*
      ** Now we finally know where the item is going so go ahead and
      **  add it.
      **
      */
      rxUsart[channel].items[rxUsart[channel].current_top] = data;

      /*
      ** If the top has reached the bottom of the list, then we must
      **  loop back around to the top, otherwise, just come down
      **  one element in the list.
      **
      */
      if( rxUsart[channel].current_top == 0 )
      {
         rxUsart[channel].current_top = USART_BUFFER_SIZE - 1;
      }
      else
      {
         rxUsart[channel].current_top--;

      } /* endif( list->current_top == 0 ) */

   } /* endif( positions_left > 0 ) */

}

uint8 rxUsartGet(uint8 channel)
{
   /*
   ** Return a zero by default
   **
   */
   uint8 retVal = 0xfe;

   /*
   ** Cannot allow interrupts to occur while massaging this circular
   **  buffer's registers.
   **
   */
   DISABLE_INTERRUPTS;

   /*
   ** Only remove the item from the list if it is there
   **
   */
   if( rxUsart[channel].current > 0 )
   {
      /*
      ** We're removing an item so we must bump the item count
      **
      */
      rxUsart[channel].current--;

      /*
      ** Now we finally know where the item is coming from so go ahead and
      **  take it.
      **
      */
      retVal = rxUsart[channel].items[rxUsart[channel].next_bottom];

      /*
      ** If the bottom has reached the bottom of the list, then we must
      **  loop back around to the top, otherwise, just come down
      **  one element in the list.
      **
      */
      if( rxUsart[channel].next_bottom == 0 )
      {
         rxUsart[channel].next_bottom = USART_BUFFER_SIZE - 1;
      }
      else
      {
         rxUsart[channel].next_bottom--;

      } /* endif( list->next_bottom == 0 ) */

   } /* endif( positions_left > 0 ) */

   /*
   ** Return the result of our actions -- remember interrupts are
   **  still turned off.
   **
   */
   return( retVal );

}

ISR(USART0_RX_vect)
{
  rxUsartPut( 0, UDR0 );
}

ISR(USART1_RX_vect)
{
  rxUsartPut( 1, UDR1 );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


usart_t txUsart[2];

//#ifdef TX_USART_BUFFER_ADDRESS
//#locate txUsart = TX_USART_BUFFER_ADDRESS
//#endif

void txUsartReset(uint8 channel)
{
  DISABLE_INTERRUPTS;
  txUsart[channel].status       = 0;
  txUsart[channel].current      = 0;
  txUsart[channel].current_top  = 0;
  txUsart[channel].next_bottom  = 0;
}

uint8 txUsartCount(uint8 channel)
{
  return( txUsart[channel].current );
}

uint8 txUsartSpace(uint8 channel)
{
  return( USART_BUFFER_SIZE - txUsart[channel].current );
}

void txUsartPut( uint8 channel, uint8 data )
{
   DISABLE_INTERRUPTS;

   if( txUsart[channel].current < USART_BUFFER_SIZE )
   {
      txUsart[channel].current++;

      txUsart[channel].items[txUsart[channel].current_top] = data;

      if( txUsart[channel].current_top == 0 )
      {
         txUsart[channel].current_top = USART_BUFFER_SIZE - 1;
      }
      else
      {
         txUsart[channel].current_top--;
      }

      /*
      ** Turn on the interrupts for transmit data ready.
      **  This allows the TX ISP to pick up the data from
      **  the circular list.
      ** xyzzy
      */
      if( channel == 0 ) TX_USART0_INTERRUPT_ENABLE;
      if( channel == 1 ) TX_USART1_INTERRUPT_ENABLE;
   }

}

uint8 txUsartGet(uint8 channel)
{
   uint8 retVal = 0;

   DISABLE_INTERRUPTS;

   if( txUsart[channel].current > 0 )
   {
      txUsart[channel].current--;

      retVal = txUsart[channel].items[txUsart[channel].next_bottom];

      if( txUsart[channel].next_bottom == 0 )
      {
         txUsart[channel].next_bottom = USART_BUFFER_SIZE - 1;
      }
      else
      {
         txUsart[channel].next_bottom--;
      }
   }

   return( retVal );

}

void init_usart(uint8 channel, uint8 baud)
{
  DISABLE_INTERRUPTS;

  /*
  ** Set up the serial port from the documentation (2545M AVR 09/07)
  **
  */
  rxUsartReset(channel);
  txUsartReset(channel);

  /*
  ** USART Control and Status Register A pp191 (2545M AVR 09/07)
  **
  */
  if( channel == 0 )
  {
    UCSR0A = 
      (0b0   << RXC0)    |  // 7.USART Receive Complete
      (0b0   << TXC0)    |  // 6.USART Transmit Complete
      (0b0   << UDRE0)   |  // 5.USART Data Register Empty
      (0b0   << FE0)     |  // 4.Frame Error
      (0b0   << DOR0)    |  // 3.Data Overrun
      (0b0   << UPE0)    |  // 2.Parity Error
      (0b0   << U2X0)    |  // 1.Double the USART Tranmission Speed
      (0b0   << MPCM0)   ;  // 0.Multi-Processor Communication Mode

    /*
    ** USART Control and Status Register B pp192 (2545M AVR 09/07)
    **
    */
    UCSR0B =
      (0b1   << RXCIE0)  |  // 7.RX Complete Interrupt Enable
      (0b0   << TXCIE0)  |  // 6.TX Complete Interrupt Enable
      (0b0   << UDRIE0)  |  // 5.USART Data Register Empty Interrupt Enable
      (0b1   << RXEN0)   |  // 4.Receiver Enable
      (0b1   << TXEN0)   |  // 3.Transmitter Enable
      (0b0   << UCSZ02)  |  // 2.Character Size
      (0b0   << RXB80)   |  // 1.Receive Data Bit 8
      (0b0   << TXB80)   ;  // 0.Transmit Data Bit 8

    /*
    ** USART Control and Status Register C pp193 (2545M AVR 09/07)
    **
    */
    UCSR0C =
      (0b00  << UMSEL00) | // 6.Mode Select (0=Asynchronous)
      (0b00  << UPM00)   | // 4.Parity Mode (0=none)
      (0     << USBS0)   | // 3.Stop Bit (0=1-bit, 1=2-bit)
      (0b011 << UCSZ00)  ; // 0.Character Size (0b011=8-bit)

    /*
    ** Baud Rate Generator
    **
    ** pp199 table 19-12
    ** 20Mhz, 19200 baud
    ** UBRRn = 64    U2Xn = 0
    ** UBRRn = 129   U2Xn = 1
    **
    ** 20Mhz, 57600 baud
    ** UBRRn = 21    U2Xn = 0
    **
    ** 20Mhz, 115200 baud
    ** UBRRn = 10    U2Xn = 0
    */
    UBRR0 = baud;

  }


  /*
  ** USART Control and Status Register A pp191 (2545M AVR 09/07)
  **
  */
#ifdef UCSR1A
  if( channel == 1 )
  {
    UCSR1A = 
      (0b0   << RXC1)    |  // 7.USART Receive Complete
      (0b0   << TXC1)    |  // 6.USART Transmit Complete
      (0b0   << UDRE1)   |  // 5.USART Data Register Empty
      (0b0   << FE1)     |  // 4.Frame Error
      (0b0   << DOR1)    |  // 3.Data Overrun
      (0b0   << UPE1)    |  // 2.Parity Error
      (0b0   << U2X1)    |  // 1.Double the USART Tranmission Speed
      (0b0   << MPCM1)   ;  // 0.Multi-Processor Communication Mode

    /*
    ** USART Control and Status Register B pp192 (2545M AVR 09/07)
    **
    */
    UCSR1B =
      (0b1   << RXCIE1)  |  // 7.RX Complete Interrupt Enable
      (0b0   << TXCIE1)  |  // 6.TX Complete Interrupt Enable
      (0b0   << UDRIE1)  |  // 5.USART Data Register Empty Interrupt Enable
      (0b1   << RXEN1)   |  // 4.Receiver Enable
      (0b1   << TXEN1)   |  // 3.Transmitter Enable
      (0b0   << UCSZ12)  |  // 2.Character Size
      (0b0   << RXB81)   |  // 1.Receive Data Bit 8
      (0b0   << TXB81)   ;  // 0.Transmit Data Bit 8

    /*
    ** USART Control and Status Register C pp193 (2545M AVR 09/07)
    **
    */
    UCSR1C =
      (0b00  << UMSEL00) | // 6.Mode Select (0=Asynchronous)
      (0b00  << UPM00)   | // 4.Parity Mode (0=none)
      (0     << USBS0)   | // 3.Stop Bit (0=1-bit, 1=2-bit)
      (0b011 << UCSZ00)  ; // 0.Character Size (0b011=8-bit)

    /*
    ** Baud Rate Generator
    **
    ** pp199 table 19-12
    ** 20Mhz, 19200 baud
    ** UBRRn = 64    U2Xn = 0
    ** UBRRn = 129   U2Xn = 1
    **
    ** 20Mhz, 57600 baud
    ** UBRRn = 21    U2Xn = 0
    **
    ** 20Mhz, 115200 baud
    ** UBRRn = 10    U2Xn = 0
    */
    UBRR1 = baud;

  }
#endif







  /*
  ** USART Control and Status Register A pp191 (2545M AVR 09/07)
  **
  */
  UCSR1A = 
    (0b0   << RXC1)    |  // 7.USART Receive Complete
    (0b0   << TXC1)    |  // 6.USART Transmit Complete
    (0b0   << UDRE1)   |  // 5.USART Data Register Empty
    (0b0   << FE1)     |  // 4.Frame Error
    (0b0   << DOR1)    |  // 3.Data Overrun
    (0b0   << UPE1)    |  // 2.Parity Error
    (0b0   << U2X1)    |  // 1.Double the USART Tranmission Speed
    (0b0   << MPCM1)   ;  // 0.Multi-Processor Communication Mode

  /*
  ** USART Control and Status Register B pp192 (2545M AVR 09/07)
  **
  */
  UCSR1B =
    (0b1   << RXCIE1)  |  // 7.RX Complete Interrupt Enable
    (0b0   << TXCIE1)  |  // 6.TX Complete Interrupt Enable
    (0b0   << UDRIE1)  |  // 5.USART Data Register Empty Interrupt Enable
    (0b1   << RXEN1)   |  // 4.Receiver Enable
    (0b1   << TXEN1)   |  // 3.Transmitter Enable
    (0b0   << UCSZ12)  |  // 2.Character Size
    (0b0   << RXB81)   |  // 1.Receive Data Bit 8
    (0b0   << TXB81)   ;  // 0.Transmit Data Bit 8

  /*
  ** USART Control and Status Register C pp193 (2545M AVR 09/07)
  **
  */
  UCSR1C =
    (0b00  << UMSEL00) | // 6.Mode Select (0=Asynchronous)
    (0b00  << UPM00)   | // 4.Parity Mode (0=none)
    (0     << USBS0)   | // 3.Stop Bit (0=1-bit, 1=2-bit)
    (0b011 << UCSZ00)  ; // 0.Character Size (0b011=8-bit)

  /*
  ** Baud Rate Generator
  **
  ** pp199 table 19-12
  ** 20Mhz, 19200 baud
  ** UBRRn = 64    U2Xn = 0
  ** UBRRn = 129   U2Xn = 1
  **
  ** 20Mhz, 57600 baud
  ** UBRRn = 21    U2Xn = 0
  **
  ** 20Mhz, 115200 baud
  ** UBRRn = 10    U2Xn = 0
  */
  UBRR1 = 64;






}


/*!
** \brief Service interrupt for Transmit Data Buffer Empty
**
** This routine is rediculously simple.  It is responding to the
**  Transmit Buffer Empty interrupt.  Upon interrupt, if there is
**  data waiting in the transmit circular list, it is pulled from
**  the list and sent to the Uart Data Register.  If there is no
**  data then the interrupt mask itself it turned off.
**
*/
ISR(USART0_UDRE_vect)
{
  if( txUsart[0].current > 0 )
  {
    txUsart[0].current--;

    UDR0 = txUsart[0].items[txUsart[0].next_bottom];

    if( txUsart[0].next_bottom == 0 )
    {
       txUsart[0].next_bottom = USART_BUFFER_SIZE - 1;
    }
    else
    {
       txUsart[0].next_bottom--;
    }

  }
  else
  {
    TX_USART0_INTERRUPT_DISABLE;
  }
}

ISR(USART1_UDRE_vect)
{
  if( txUsart[1].current > 0 )
  {
    txUsart[1].current--;

    UDR1 = txUsart[1].items[txUsart[1].next_bottom];

    if( txUsart[1].next_bottom == 0 )
    {
       txUsart[1].next_bottom = USART_BUFFER_SIZE - 1;
    }
    else
    {
       txUsart[1].next_bottom--;
    }

  }
  else
  {
    TX_USART1_INTERRUPT_DISABLE;
  }
}





